home *** CD-ROM | disk | FTP | other *** search
- { FADS: Find - Attributes - DateTime - Size }
- { Compile with Borland Pascal 7.0
- no compiler directives required. }
-
- USES DOS, CRT;
-
- CONST Version = '1.1';
- Extra = '(Freeware by Al)';
-
- { This is the main constant limiting the expandability of the program }
- MaxDirs = 3072;
-
- { Length of string needed to hold file information string
- (minimum size : Max. Length of a Directory }
- LenInfoStr = 90;
-
- { assume 25-row screen (for now) }
- MaxRows = 25;
-
- { Chameleon constants }
- ProgFD = 1; { FileDate }
- ProgLD = 2; { ListDirs }
- ProgFF = 3; { FindFile }
- ProgFA = 4; { FindAttr }
- ProgLC = 5; { LineCounter }
- MaxProg = 5;
- MaxNames = 3;
- ProgNameArray : ARRAY[ 1..MaxProg, 1..MaxNames ] OF NameStr =
- ( ( 'FD', 'FILEDATE', 'TOUCH' ),
- ( 'LD', 'LISTDIR', '' ),
- ( 'FF', 'FILEFIND', 'FINDFILE' ),
- ( 'FA', 'FILEATTR', '' ),
- ( 'LC', 'COUNTLNS', '' ) );
-
- { Used by FF & FA for attribute handling }
- AttrSys = #1; { Handled in ChkFlag }
- AttrChar : ARRAY[ 1..4 ] OF CHAR = ( 'R', 'A', AttrSys, 'H' );
- AttrVal : ARRAY[ 1..4 ] OF BYTE = ( ReadOnly, Archive, SysFile, Hidden ); {BP constants}
-
- { Cmd.. & Val.. for Touch }
- CmdValNone = 0; { No /D or /T switch on command line }
- CmdValFlagOnly = -1; { /D or /T, but without a specified date/time }
- CmdValArg = 1; { /D or /T with a specified date/time }
- ValFile = 0; { retain date OR time from file itself }
- ValSys = 1; { use system date OR time for new value }
- ValArg = 2; { use specified argument date OR time for new value }
-
- { for multiple filespecs passed to FF or COUNTLNS
- - undocumented except here.
- Note: path is retrieved from first non-flag parameter only }
- MaxSpecs = 10; { should be plenty }
-
- { repeated strings }
- UserBreak = 'User Break';
- Proceed = 'Proceed (Y/N) -> ';
- NullStr = '';
-
- { for cluster size scenario }
- MaxClustChk = 5; { will be used as 1K * 2 ^ MaxClustChk }
-
- { for output options }
- MaxOutOpts = 7; { Note: Code is dependent on this being a SINGLE digit }
-
- { busy symbols }
- Busy : ARRAY[ 1..4 ] OF CHAR = ( '/', '─', '\', '|' );
-
- TYPE Str2 = STRING[ 2 ];
- Str3 = STRING[ 3 ];
- Str4 = STRING[ 4 ];
- Str8 = STRING[ 8 ];
- Str12 = STRING[ 12 ];
- Str16 = STRING[ 16 ];
- Str20 = STRING[ 20 ];
- InfoStr = STRING[ LenInfoStr ];
-
- PathRec = RECORD { our doggone favorite record type }
- P : InfoStr;
- TF : BOOLEAN;
- END; { RECORD PathRec }
- PathRecPtr = ^PathRec; { a pointer to our doggone favorite record type }
- IndexArray = ARRAY[ 1..MaxDirs ] OF PathRecPtr; { a whole bunch of pointers, yippee! }
-
- (* @@ Scrn - 4K too much memory to give up
- ScrnRec1 = RECORD
- SB : ARRAY[ 1..25 , 1..80, 0..1 ] OF BYTE;
- Y, X : BYTE;
- VideoMem : WORD;
- END; { RECORD ScrnRec1 }
- *)
-
- VAR ArgDT : DateTime; { Command Line argument DateTime record }
- AttrFlag : ARRAY[ 1..4 ]
- OF SHORTINT; { Flags to tell FF/FA to look for/set att's }
- AutoYesTF : BOOLEAN; { Automatically answer Yes to prompt T/F }
- BlankLine : STRING; { 79 spaces, do you care? }
- Capacity : LongInt; { Size of disk }
- CD : DirStr; { current directory at start }
- ClusterTF : BOOLEAN; { show "cluster size scenario" }
- Clusticity : ARRAY[ 1..2 ]
- OF LONGINT; { Cluster size of ( 2 ) disks }
- CmdLine : STRING; { Command line entered by user }
- CodeDate, CodeTime : SHORTINT; { Flags (a) check parameters on start; (b) setting file Date/Time }
- CountLnsTF : BOOLEAN; { count lines instead of cluster-space }
- DirTotal : WORD; { # of directories involved /S }
- DrvSource : CHAR; { Source drive (from dir #1)- FF }
- DrvTarget : CHAR; { Target drive - FF }
- EnvVar : STRING; { alt. sets of directories to scan }
- ExitDirNum : WORD; { # of directory to exit to - FF }
- FileTotal : WORD; { # of files (and directories) involved (FF only }
- ForcePromptTF : BOOLEAN; { Force prompting of user even if only one file }
- FW : TEXT; { File handle for OutputFile, if used }
- Index : IndexArray; { for storing list of directories /S }
- LinesTotal : LONGINT; { Total # of lines counted if CountLnsTF set }
- NumFiles : WORD; { # of files to touch }
- ProgramName : NameStr; { Name of Program }
- ProgramNum : BYTE; { Number of Program: Progxx above }
- PS : PathStr; { command line filespec }
- PSadd : ARRAY[ 1..MaxSpecs ]
- OF Str12; { additional filespec for FF & LC }
- PSaddCount : BYTE; { # of additional filespecs }
- OutputFile : PathStr; { output file ( valid only for FF, LD, LC ) }
- OutFlag : ARRAY[ 1..MaxOutOpts ]
- OF BOOLEAN; {/W1 - include command line echo
- 2 - include # of directories
- 3 - include directory name w/o number
- 4 - include directory name with #.
- 5 - include file listing
- 6 - include # of files found
- 7 - include bytes summary info
- default : ALL
- 3 & 4 are mutually exclusive ( 4 overrides ) }
- OutOverAutoTF : BOOLEAN; { automatic overwrite Output file (no prompt) }
- OutputTF : BOOLEAN; { Flag to signal Output File }
- (* @@ Scrn
- Scrn : ScrnRec1; { saved screen - 4KB }
- *)
- SpaceCluster : ARRAY[ 1..MaxClustChk, 1..2 ]
- OF LONGINT; { 1 - total size of files ( ,1 )
- and tree structure ( ,2 )
- using smallest HD cluster of 2K
- ( up to 128 MB partition )
- 2 - same using 4K ( up to 256 MB )
- 3 - same using 8K ( up to 512 MB )
- 4 - same using 16K ( up to 1GB )
- 5 - same using 32K ( up to 2GB ) }
- SpaceTotal : ARRAY[ 0..2 ]
- OF LONGINT; { 0 - total size of files
- 1 - total space of files on source
- 2 - total space of files on source
- including dir structure }
- SubDirTF : BOOLEAN; { Include subdirectories T/F }
- SysDT : DateTime; { System clock's DateTime record }
- TargetSize : ARRAY[ 1..2 ]
- OF LONGINT; { 1 = space available (free) on target
- 2 = size of target disk }
- TargetSpace : ARRAY[ 1..2 ]
- OF LONGINT; { 1 = total space req'd on target
- (files only - no directories)
- 2 = total space req'd on target
- including dir structure }
- UserDateStr : STRING; { string to present to user at confirmation prompt }
- UserTimeStr : STRING; { string to present to user at confirmation prompt }
- YY : BYTE; { Screen Y for output }
- ZipScanTF : BOOLEAN; { Scan ZIP files when FF'ing a file }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ General purpose functions used by FADS.Pas ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- { general function: returns larger of two numbers }
- FUNCTION Max( W1, W2 : WORD ) : WORD;
- BEGIN
- IF ( W1 > W2 ) THEN Max := W1 ELSE Max := W2;
- END; { Max }
-
- { general function: returns smaller of two numbers }
- FUNCTION Min( W1, W2 : WORD ) : WORD;
- BEGIN
- IF ( W1 < W2 ) THEN Min := W1 ELSE Min := W2;
- END; { Min }
-
- { general function: returns position of last character Ch in String }
- FUNCTION LastPos( Ch : CHAR; S : STRING ) : BYTE;
- VAR I : BYTE;
- BEGIN
- FOR I := LENGTH( S ) DOWNTO 1 DO IF ( S[ I ] = Ch ) THEN BEGIN
- LastPos := I;
- EXIT;
- END;
- LastPos := 0;
- END; { LastPos }
-
- { general function: returns upper case version of String }
- FUNCTION UpStr( AA : STRING ) : STRING;
- VAR I : BYTE;
- BEGIN
- FOR I := 1 TO LENGTH( AA ) DO AA[ I ] := UpCase( AA[ I ] );
- UpStr := AA;
- END; { UpStr }
-
- { general function: returns string with zeroes substituted for spaces }
- FUNCTION Zero( A0 : Str12 ) : Str12;
- VAR ZI : BYTE;
- BEGIN
- FOR ZI := 1 TO 2 DO IF ( A0[ ZI ] = #32 ) THEN A0[ ZI ] := '0';
- Zero := A0;
- END; { Zero }
-
- { general function: formatted date or time string }
- FUNCTION DateTimeStr( W1, W2, W3 : WORD ) : Str12;
- VAR Ch : CHAR;
- S1, S2, S3 : Str12;
- L : BYTE;
- BEGIN
- STR( W1 : 2, S1 );
- STR( W2 : 2, S2 );
- IF ( W3 > 59 ) THEN BEGIN { assume date string if W3 is greater than 59 }
- L := 4;
- Ch := '-';
- END ELSE BEGIN
- L := 2;
- Ch := ':';
- END;
- STR( W3 : L, S3 );
- DateTimeStr := Zero( S1 ) + Ch + Zero( S2 ) + Ch + Zero( S3 );
- END; { DateTimeStr }
-
- { general function: returns String with leading & trailing spaces removed }
- FUNCTION Trim( AA : STRING ) : STRING;
- VAR I : BYTE;
- BEGIN
- WHILE ( COPY( AA, 1, 1 ) = ' ') DO AA := COPY( AA, 2, 255 );
- WHILE ( COPY( AA, LENGTH( AA ), 1 ) = ' ')
- DO AA := COPY( AA, 1, LENGTH( AA ) - 1 );
- Trim := AA;
- END; { Trim }
-
- { general function: produces String of char(s) AA of RJ * LEN(AA) length }
- FUNCTION Replicate( AA : STRING; RJ : BYTE ) : STRING;
- VAR RR : STRING;
- RI : BYTE;
- BEGIN
- RR := '';
- FOR RI := 1 TO RJ DO RR := RR + COPY( AA, 1, 1 );
- Replicate := RR;
- END; { Replicate }
-
- { general function: returns String padded with char Ch to length L
- if LeftPadTF (spaces added to left side);
- default: add spaces to right }
- FUNCTION PadChar( AAA : STRING; L : BYTE; Ch : CHAR; LeftPadTF : BOOLEAN )
- : STRING;
- BEGIN
- IF ( LENGTH( AAA ) < L ) THEN BEGIN
- IF LeftPadTF THEN PadChar := Replicate( Ch, L - LENGTH( AAA ) ) + AAA
- ELSE PadChar := AAA + Replicate( Ch, L - LENGTH( AAA ) );
- END ELSE PadChar := COPY( AAA, 1, L );
- END; { PadChar }
-
- { general function: returns String padded with spaces on left to length L }
- FUNCTION PadL( AAA : STRING; L : BYTE ) : STRING;
- BEGIN
- PadL := PadChar( AAA, L, #32, TRUE );
- END; { PadL }
-
- { general function: returns String padded with spaces on right to length L }
- FUNCTION PadR( AAA : STRING; L : BYTE ) : STRING;
- BEGIN
- PadR := PadChar( AAA, L, #32, FALSE );
- END; { PadR }
-
- { general function: returns filename (incl. wildcard) as a 12-character
- string with "?" in "wild" positions }
- FUNCTION File12( M : Str12 ) : Str12;
- VAR MM : Str12;
- MI : BYTE;
- MP : BOOLEAN;
- BEGIN
- MM := '';
- MP := FALSE;
- FOR MI := 1 TO LENGTH( M ) DO CASE M[ MI ] OF
- '*' : IF MP THEN MM := MM + REPLICATE( '?', 12 - LENGTH( MM ) )
- ELSE MM := MM + REPLICATE( '?', 8 - LENGTH( MM ) );
- '.' : BEGIN
- MM := PadR( MM, 8 ) + '.';
- MP := TRUE;
- END;
- ELSE MM := MM + UpCase( M[ MI ] );
- END; { CASE MI }
- File12 := PadR( MM, 12 );
- END; { File12 }
-
- { general function: tests the first filename S1 against the template S2
- Note: Order of S1 & S2 significant }
- FUNCTION Match( S1, S2 : Str12 ) : BOOLEAN;
- VAR M : BOOLEAN;
- MM : BYTE;
- BEGIN
- M := TRUE;
- FOR MM := 1 TO 12 DO IF ( ( S1[ MM ] <> S2[ MM ] ) AND ( S2[ MM ] <> '?' ) )
- THEN M := FALSE;
- Match := M;
- END;
-
- { general function: add a filename/directory to a path }
- FUNCTION FullName(Dir : DirStr; Fname : Str12) : PathStr;
- BEGIN
- IF ( Dir[ LENGTH( Dir ) ] = '\' ) THEN FullName := Dir + Fname
- ELSE FullName := Dir + '\' + Fname;
- END; { FullName }
-
- { general function: returns String with commas inserted }
- FUNCTION PrintUsing( N : LONGINT ) : Str20;
- VAR S1, S2 : Str12;
- BEGIN
- IF ( N = 0 ) THEN PrintUsing := '0' ELSE BEGIN
- STR( N : TRUNC( LN( N ) / LN( 10 ) ) + 1, S1 );
- S2 := '';
- WHILE ( LENGTH( S1 ) > 3 ) DO BEGIN
- S2 := COPY( S1, LENGTH( S1 ) - 2, 3 ) + S2;
- S1 := COPY( S1, 1, LENGTH( S1 ) - 3 );
- S2 := ',' + S2;
- END;
- PrintUsing := S1 + S2;
- END;
- END; { PrintUsing }
-
- { general function: returns # of bytes in a cluster on a disk }
- { DrvNo : 0 = default drive; 1 = A, 2 = B, ... (just like BP7's DiskSize ) }
- FUNCTION ByteClust( DrvNo : BYTE ) : WORD;
- VAR BCregs : Registers;
- BC : LongInt;
- BEGIN
- BCregs.AX := $3600;
- BCregs.DX := DrvNo;
- MsDos( BCregs );
- BC := BCregs.AL * BCregs.CX;
- ByteClust := BC;
- END; { ByteClust }
-
- { general function: returns lowest multiple of ClustSize >= ActualSize }
- FUNCTION ByteAdjust( ActualSize, ClustSize : LongInt ) : LongInt;
- VAR BAret : REAL;
- BAint : REAL;
- BEGIN
- IF ( ClustSize = 0 ) THEN BAret := 0 ELSE BEGIN
- BAint := INT( ActualSize / ClustSize );
- BAret := BAint * ClustSize;
- IF ( BAret <> ActualSize ) THEN BAret := BAret + ClustSize;
- END;
- ByteAdjust := ROUND( BAret );
- END; { ByteAdjust }
-
- { general function: returns % of SpaceUsed not use for FileContents }
- FUNCTION WastedSpace( ActualSize, SpaceUsed : LongInt ) : Str12;
- VAR BA : REAL;
- S12 : Str12;
- BEGIN
- BA := ( ( SpaceUsed - ActualSize ) / SpaceUsed ) * 100;
- STR( BA : 3 : 2, S12 );
- WastedSpace := S12;
- END; { WastedSpace }
-
- { general function: returns number raised to a power }
- FUNCTION Power( Number : LongInt; Exponent : BYTE ) : LongInt;
- VAR I : BYTE;
- Ret : LONGINT;
- BEGIN
- Ret := 1;
- FOR I := 1 TO Exponent DO Ret := Ret * Number;
- Power := Ret;
- END; { Power }
-
- { general function: returns attribute value as a string of 4 chars in the
- form RASH, using a "." if a specific attribute is not
- set }
- FUNCTION AttributeStr( Attr : BYTE ) : Str12;
- VAR AttrStr : Str12;
- BEGIN
- AttrStr := '';
- IF ( Attr AND ReadOnly = ReadOnly )
- THEN AttrStr := AttrStr + 'r'
- ELSE AttrStr := AttrStr + '.';
- IF ( Attr AND Archive = Archive )
- THEN AttrStr := AttrStr + 'a'
- ELSE AttrStr := AttrStr + '.';
- IF ( Attr AND SysFile = SysFile )
- THEN AttrStr := AttrStr + 's'
- ELSE AttrStr := AttrStr + '.';
- IF ( Attr AND Hidden = Hidden )
- THEN AttrStr := AttrStr + 'h'
- ELSE AttrStr := AttrStr + '.';
- AttributeStr := AttrStr;
- END; { AttributeStr }
-
- (* @@ Scrn
- { general function: save current screen to Scrn variable }
- PROCEDURE ScrGet1;
- VAR I, J, K : BYTE;
- Reg : Registers;
- BEGIN
- IF ( Mem[ $40 : $63 ] = $B4 ) THEN Scrn.VideoMem := $B000 {monochrome}
- ELSE Scrn.VideoMem := $B800; {color}
- FOR I:= 1 TO 25 DO FOR J := 1 TO 80 DO FOR K := 0 TO 1 DO
- Scrn.SB[ I, J, K ] :=
- MEM[ Scrn.VideoMem : ( I - 1 ) * 160 + ( J - 1 ) * 2 + K ];
- Scrn.Y := WHEREY;
- Scrn.X := WHEREX;
- END; { ScrGet1 }
-
- { general function: restore saved screen from Scrn variable }
- PROCEDURE ScrPut1( CursorBack : BOOLEAN );
- VAR I, J, K : BYTE;
- BEGIN
- FOR I := 1 TO 25 DO FOR J := 1 TO 80 DO FOR K := 0 TO 1 DO
- MEM[ Scrn.VideoMem : ( I - 1 ) * 160 + ( J - 1 ) * 2 + K ] :=
- Scrn.SB[ I, J, K ];
- IF CursorBack THEN GOTOXY( Scrn.X, Scrn.Y );
- END; { ScrPut1 }
- *)
-
- { general function: check for ESC or ^C by user
- Confirm Quit if ESC
- Note: Uses global CD (current dir) & UserBreak var }
- PROCEDURE UserEscQuit;
- CONST QuitStr = ' Quit now? ( Y / N ) -> ';
- VAR Ch : CHAR;
- YY, XX : BYTE;
- BEGIN
- IF KeyPressed THEN BEGIN
- Ch := READKEY;
- IF ( Ch = #27 ) THEN BEGIN
- YY := WHEREY;
- XX := WHEREX;
- Write( QuitStr );
- REPEAT
- Ch := UpCase( READKEY );
- UNTIL ( Ch IN [ 'Y', 'N', #27, #32 ] );
- GOTOXY( XX, YY );
- Write( Replicate( #32, LENGTH( QuitStr ) ) );
- GOTOXY( XX, YY );
- IF ( Ch = 'Y' ) THEN Ch := #3;
- END;
- IF ( Ch = #3 ) THEN BEGIN
- WriteLn;
- WriteLn( UserBreak );
- ChDir( CD );
- HALT;
- END;
- END; { Keypressed }
- END; { UserEscQuit }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ Error & Help messages ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE Error( Msg : STRING );
- BEGIN
- WriteLn( Msg );
- HALT( 1 ); { return an errorlevel of 1 to DOS for batchfile processing }
- END; { Error }
-
- PROCEDURE ChameleonHelp;
- CONST Desc : ARRAY[ 1..MaxProg ] OF PathStr =
- ( 'A "Touch" (file-date/timestamp modifier) program',
- 'A directory lister',
- 'A file-finder (that totals file sizes)',
- 'A file-attributer modifier',
- 'A counter of lines in textfiles' );
- VAR S3 : Str3;
- I, J : BYTE;
- BEGIN
- WriteLn;
- STR( MaxProg, S3 );
- WriteLn( 'Chameleon Help: this program can act in 1 of ' + S3 + ' ways:' );
- FOR I := 1 TO 5 DO BEGIN
- STR( I, S3 );
- WriteLn( ' ', S3, '. ', Desc[ I ] );
- Write( ' if the filename is one of these: ' );
- FOR J := 1 TO MaxNames DO BEGIN
- IF NOT ( ProgNameArray[ I, J ] = '' ) THEN BEGIN
- IF ( J > 1 ) THEN Write( ',' );
- Write( ' ', ProgNameArray[ I, J ] );
- END;
- END;
- WriteLn;
- END; { FOR I }
- WriteLn( 'Alternatively, set the environment variable FADS to one of the above names.' );
- WriteLn( 'The environment variable overrides the filename.' );
- HALT;
- END; { ChameleonHelp }
-
- PROCEDURE Help;
- CONST ChameleonKey : CHAR = 'C';
- IncSubs = ' /S Include Subdirectories.';
- AutoYes = ' /Y Automatically answer Yes to confirmation prompt.';
- ForcePrompt = ' /1 Force confirmation prompt even if only one file.';
- Dumb = 'Including both /Y and /1 is dumb, and skips the confirmation prompt.';
- DontPause = ' /Y Don''t pause after each screenful.';
- Unless = ' you are prompted to confirm, unless /Y is included.';
- Output = ' /W:file Write output to file as well as to screen. Assumes /Y.';
- OutOver = ' /WO Overwrite output file (if it exists) without prompting.';
- OutW1 = ' 1 - echo command line';
- OutW2 = ' 2 - Starting directory & # of directories found';
- OutW3 = ' 3 - Directories (without number)';
- OutW4 = ' 4 - Directories with leading number and period (overrides 3)';
- VAR XX : BYTE;
- Ch : CHAR;
- ChamString : STRING;
- FlagStr : DirStr;
- LineNum : BYTE;
-
- PROCEDURE HelpLine( HLS : STRING );
- CONST More = '- More - Press a key -';
- VAR Ch : CHAR;
- BEGIN
- INC( LineNum );
- IF ( LineNum = ( MaxRows - 1 ) ) THEN BEGIN
- Write( More );
- Ch:= READKEY;
- IF KeyPressed THEN Ch:= READKEY;
- GOTOXY( 1, WHEREY );
- Write( Replicate( #32, LENGTH( More ) ) );
- GOTOXY( 1, WHEREY );
- END;
- WriteLn( HLS );
- END; { HelpLine }
-
- BEGIN { Help }
- ChamString := 'Press ' + ChameleonKey +
- ' for information on Chameleon feature of program. Any other key exits.';
- LineNum := 0;
- CASE ProgramNum OF
- ProgFD : BEGIN
- HelpLine( ProgramName + ' will modify the timestamp (date & time) of a file.' );
- HelpLine( NullStr );
- HelpLine( 'Syntax: ' + ProgramName + ' pathname [/D[date]] [/T[time]] [/S] [/Y] [/1]' );
- HelpLine( NullStr );
- HelpLine( ' /D[mm-dd-yy] Set the file date to [mm-dd-yy]' );
- HelpLine( ' /T[hh:mm:ss] Set the file time to [hour:minute:second]' );
- HelpLine( IncSubs );
- HelpLine( AutoYes );
- HelpLine( ForcePrompt );
- HelpLine( NullStr );
- HelpLine( 'NOTES: 1. If more than one file''s timestamp would be modified,' );
- HelpLine( Unless );
- HelpLine( ' 2. Files flagged ReadOnly, Hidden or System are not touched.' );
- HelpLine( ' 3. If you do not include /D or /T, ' + ProgramName + ' will set the file''s timestamp' );
- HelpLine( ' to that of the current date & time.' );
- HelpLine( ' 4. Hour:minute:second can be from 00:00:00 to 23:59:59.' );
- HelpLine( ' 5. ' + Dumb );
- END; { ProgFD }
- ProgFA : BEGIN
- HelpLine( ProgramName + ' will modify the attributes of a file.' );
- HelpLine( NullStr );
- Write( 'Syntax: ' + ProgramName + ' pathname ' );
- XX := WHEREX;
- HelpLine( '[/A+|/A-] [/R+|/R-] [/H+|/H-] [/SYS+|/SYS-]' );
- GOTOXY( XX, WHEREY );
- HelpLine( '[/S] [/Y] [/1]' );
- HelpLine( NullStr );
- HelpLine( ' /A+ Sets Archive attribute' );
- HelpLine( ' /A- Removes Archive attribute' );
- HelpLine( ' /R+ Sets ReadOnly attribute' );
- HelpLine( ' /R- Removes ReadOnly attribute' );
- HelpLine( ' /H+ Sets Hidden attribute' );
- HelpLine( ' /H- Removes Hidden attribute' );
- HelpLine( ' /SYS+ Sets System attribute' );
- HelpLine( ' /SYS- Removes System attribute' );
- HelpLine( IncSubs );
- HelpLine( AutoYes );
- HelpLine( ForcePrompt );
- HelpLine( NullStr );
- HelpLine( 'NOTES: 1. If more than one file''s attributes would be modified,' );
- HelpLine( Unless );
- HelpLine( ' 2. ' + Dumb );
- END; { ProgFA }
- ProgFF : BEGIN
- Write( ProgramName + #32 );
- XX := WHEREX;
- Write( 'finds files matching the pathname and specified attributes (if any)' );
- IF CountLnsTF THEN BEGIN
- FlagStr := '[/S] ';
- HelpLine( NullStr );
- GOTOXY( XX, WHEREY );
- HelpLine( 'and counts the number of lines found in each.' );
- END ELSE BEGIN
- FlagStr := '[/L] [/FIT:drive] [/C] ';
- HelpLine( '.' );
- END;
- Write( 'Syntax: ' + ProgramName + ' [pathname] ');
- XX := WHEREX;
- Write( '[/A[+/-]] [/R[+/-]] [/H[+/-]] [/SYS[+/-]]' );
- IF NOT CountLnsTF THEN Write( ' [/S-] [/Z]' );
- HelpLine( NullStr );
- GOTOXY( XX, WHEREY );
- HelpLine( '[/E:var] ' + FlagStr );
- GOTOXY( XX, WHEREY );
- HelpLine( '[/W:file] [/W[1][2][3][4][5][6][7]] [/WO] [/Y]' );
- HelpLine( ' /A or /A+ Find files with Archive attribute set' );
- HelpLine( ' /A- Find files without Archive attribute set' );
- HelpLine( ' /R or /R+ Find files with ReadOnly attribute set' );
- HelpLine( ' /R- Find files without ReadOnly attribute set' );
- HelpLine( ' /H or /H+ Find files with Hidden attribute set' );
- HelpLine( ' /H- Find files without Hidden attribute set' );
- HelpLine( ' /SYS or /SYS+ Find files with System attribute set' );
- HelpLine( ' /SYS- Find files without System attribute set' );
- IF NOT CountLnsTF THEN BEGIN
- HelpLine( ' /S- Exclude Subdirectories. (default : search subdirectories)' );
- HelpLine( ' /Z Scan through .ZIP files (sensitive to attrib) for filespec' );
- END;
- HelpLine( ' /E:var Search the only directories listed in environment variable' );
- HelpLine( ' named after E:. (e.g. /E:PATH)' );
- IF CountLnsTF THEN HelpLine( IncSubs ) ELSE BEGIN
- HelpLine( ' /L Count lines in files ( NOTE: Textfiles are assumed! ).' );
- HelpLine( ' /FIT:drive Determine space required to copy found files to drive.' );
- HelpLine( ' /C Cluster size scenarios.' );
- END;
- HelpLine( Output );
- HelpLine( ' /W[1][2][3][4][5][6][7] Write to output file (default all) e.g./W1246' );
- HelpLine( OutW1 );
- HelpLine( OutW2 );
- HelpLine( OutW3 );
- HelpLine( OutW4 );
- HelpLine( ' 5 - File information' );
- HelpLine( ' 6 - # of file found' );
- HelpLine( ' 7 - bytes summary information' );
- HelpLine( OutOver );
- HelpLine( DontPause );
- HelpLine( 'NOTES: 1. Default starting search directory is the CURRENT directory.' );
- HelpLine( ' 2. Default filespec is *.*. Additional filespecs may be added.' );
- IF NOT CountLnsTF
- THEN HelpLine( ' 3. Files found in ZIPs have compressed size listed under "Space Used".' );
- END; { ProgFF }
- ProgLD : BEGIN
- HelpLine( ProgramName + ' lists directories starting at the specified directory.' );
- HelpLine( NullStr );
- HelpLine( 'Syntax: ' + ProgramName + ' [starting directory] [/W:file] [/W[1][2][3][4]] [/WO] [/Y]');
- HelpLine( NullStr );
- HelpLine( Output );
- HelpLine( ' /W[1][2][3][4] Write to output file (default all) e.g./W23' );
- HelpLine( OutW1 );
- HelpLine( OutW2 );
- HelpLine( OutW3 );
- HelpLine( OutW4 );
- HelpLine( OutOver );
- HelpLine( DontPause );
- HelpLine( NullStr );
- HelpLine( 'NOTES: 1. The default starting directory is the CURRENT directory.' );
- END; { ProgLD }
- END; { CASE ProgramNum }
- Write( ChamString );
- Ch := UpCase( READKEY );
- GOTOXY( 1, WHEREY );
- Write( Replicate( #32, LENGTH( ChamString ) ) );
- GOTOXY( 1, WHEREY );
- IF ( Ch = ChameleonKey ) THEN ChameleonHelp;
- HALT;
- END; { Help }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ GetSys puts current date & time into global DateTime record SysDT ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE GetSys;
- VAR h, m, s, hund : Word;
- BEGIN
- GetTime( h, m, s, hund );
- SysDT.Hour := h;
- SysDT.Min := m;
- SysDT.Sec := s;
-
- GetDate( h, m, s, hund ); { re-use Word vars }
- SysDT.Year := h;
- SysDT.Month := m;
- SysDT.Day := s;
- END; { GetSys }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ Sort Array Index ( L = beginning element & R= ending element ) ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
- PROCEDURE QuickSort( VAR Index : IndexArray; L, R : WORD );
- VAR I, J : WORD;
- X, Y : PathRecPtr;
- BEGIN
- I := L;
- J := R;
- X := Index[ ( L + R ) DIV 2 ];
- REPEAT
- WHILE ( Index[ I ]^.P < X^.P ) DO INC( I );
- WHILE ( X^.P < Index[ J ]^.P ) DO DEC( J );
- IF I <= J THEN BEGIN
- Y := Index[ I ];
- Index[ I ] := Index[ J ];
- Index[ J ] := Y;
- INC( I );
- DEC( J );
- END;
- UNTIL ( I > J );
- IF L < J THEN QuickSort( Index, L, J );
- IF I < R THEN QuickSort( Index, I, R );
- END; { QuickSort }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ Directory scanning routines ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE AddNewIndex( VAR Total : WORD );
- CONST QuitStr = ' Quit now? ( Y / N ) -> ';
- VAR Ch : CHAR;
- YY, XX : BYTE;
- BEGIN
- INC( Total );
- IF ( Total > MaxDirs ) THEN
- Error( 'Exceeded maximum # of directories. Scream at programmer about MaxDirs.' );
- IF ( MaxAvail < SizeOf( PathRec ) ) THEN
- Error( 'Insufficient memory. Bummer.' );
- NEW( Index[ Total ] );
- UserEscQuit;
- END; { AddNewIndex }
-
- PROCEDURE AddDirToList( D : DirStr; CheckForDupeTF : BOOLEAN );
- VAR S12 : Str12;
- I : WORD;
- BEGIN
- IF CheckForDupeTF THEN BEGIN
- CheckForDupeTF := FALSE; { Re-use BOOLEAN variable - perfect! }
- FOR I := 1 TO DirTotal DO
- IF ( D = Index[ I ]^.P ) THEN CheckForDupeTF := TRUE;
- IF CheckForDupeTF THEN EXIT; { don't add dupe dir from user's "PATH" }
- END;
- AddNewIndex( DirTotal );
- Index[ DirTotal ]^.P := D;
- GOTOXY( 1, YY );
- IF ( DirTotal > 1 ) THEN S12 := 'ies' ELSE S12 := 'y';
- Write( DirTotal, ' director', S12, ' found.' );
- END; { AddDirToList }
-
- PROCEDURE GetDirList( SD : DirStr );
- VAR DirInfo : SearchRec;
- BEGIN
- ChDir( SD );
- FindFirst( '*.*', Directory, DirInfo );
- WHILE ( DosError = 0 ) DO BEGIN
- IF ( DirInfo.Attr AND Directory = Directory ) AND { Directory attribute }
- ( DirInfo.Name[ 1 ] <> '.' ) { No dot directories }
- THEN AddDirToList( FullName( SD, DirInfo.Name ), FALSE );
- FindNext( DirInfo );
- END;
- END; { GetDirList }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ ProcessFiles counts and, if ModifyNowTF is TRUE, updates the timestamp ║
- ║ of the files specified. ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE ProcessFiles( ModifyNowTF : BOOLEAN ); { Touch( FD ) & FA }
- VAR DirInfo : SearchRec;
- I : WORD;
- F : FILE;
- Ftime : LONGINT;
- DTstr : STRING;
- CancelTF : BOOLEAN;
- ADflag, NA : BYTE; { Access denied flag }
-
- PROCEDURE CountUpdate;
- VAR S12 : Str12;
- BEGIN
- IF ( NumFiles > 1 ) THEN S12 := 's' ELSE S12 := '';
- IF ModifyNowTF THEN BEGIN
- GOTOXY( 1, YY + 1 );
- Write( #32, NumFiles, ' file', S12, ' updated.' );
- END ELSE Write( #32, NumFiles );
- END; { CountUpdate }
-
- PROCEDURE NewFtime;
- VAR NewDT : DateTime;
- BEGIN
- IF ( ( CodeDate = ValFile ) OR ( CodeTime = ValFile ) ) THEN BEGIN
- GetFTime( F, Ftime ); { File must be OPENed to get its timestamp }
- UnpackTime( Ftime, NewDT );
- END;
- IF ( CodeDate = ValSys ) THEN BEGIN
- NewDT.Year := SysDT.Year;
- NewDT.Month := SysDT.Month;
- NewDT.Day := SysDT.Day;
- END;
- IF ( CodeDate = ValArg ) THEN BEGIN
- NewDT.Year := ArgDT.Year;
- NewDT.Month := ArgDT.Month;
- NewDT.Day := ArgDT.Day;
- END;
- IF ( CodeTime = ValSys ) THEN BEGIN
- NewDT.Hour := SysDT.Hour;
- NewDT.Min := SysDT.Min;
- NewDT.Sec := SysDT.Sec;
- END;
- IF ( CodeTime = ValArg ) THEN BEGIN
- NewDT.Hour := ArgDT.Hour;
- NewDT.Min := ArgDT.Min;
- NewDT.Sec := ArgDT.Sec;
- END;
- PackTime( NewDT, Ftime ); { set Ftime to "packed" time format }
- DTstr := DateTimeStr( NewDT.Month, NewDT.Day, NewDT.Year ) +
- Replicate( #32, 3 ) +
- DateTimeStr( NewDT.Hour, NewDT.Min, NewDT.Sec );
- END; { NewFtime }
-
- FUNCTION NewAttrib : BYTE; { Returns modified DirInfo.Attr }
- VAR NA, I : BYTE;
- BEGIN { NewAttrib }
- NA := DirInfo.Attr;
- FOR I := 1 TO 4 DO IF ( AttrFlag[ I ] <> 0 ) THEN BEGIN
- IF ( AttrFlag[ I ] = 1 ) THEN NA := NA OR AttrVal[ I ]
- ELSE IF ( AttrFlag[ I ] = -1 ) THEN
- IF ( ( NA AND AttrVal[ I ] ) > 0 ) THEN DEC( NA, AttrVal[ I ] );
- END; { FOR I }
- NewAttrib := NA;
- END; { NewAttrib }
-
- PROCEDURE AccessDenied( ADbyte : BYTE );
- CONST AD = 'Access denied. Press a key.';
- VAR XX : BYTE;
- Ch : CHAR;
- BEGIN
- ADflag := ADbyte;
- XX := WHEREX;
- Write( AD );
- IF ( NOT AutoYesTF ) THEN Ch := READKEY;
- IF ( Ch = #3 ) THEN CancelTF := TRUE;
- GOTOXY( XX, WHEREY );
- Write( Replicate( #32, LENGTH( AD ) ) );
- GOTOXY( XX, WHEREY );
- DEC( NumFiles );
- END; { AccessDenied }
-
- BEGIN { ProcessFiles }
- CancelTF := FALSE;
- NumFiles := 0;
- FOR I := 1 TO DirTotal DO BEGIN
- ChDir( Index[ I ]^.P );
- FindFirst( PS, AnyFile, DirInfo );
- WHILE ( DosError = 0 ) DO BEGIN
- IF ( DirInfo.Attr AND Directory <> Directory ) AND { Not a Directory }
- ( DirInfo.Attr AND VolumeID <> VolumeID ) THEN BEGIN { Not a Volume label }
-
- IF ( ProgramNum = ProgFA ) OR
- ( ( DirInfo.Attr AND ReadOnly <> ReadOnly ) AND { Not a ReadOnly file }
- ( DirInfo.Attr AND Hidden <> Hidden ) AND { Not a Hidden file }
- ( DirInfo.Attr AND SysFile <> SysFile ) ) THEN { Not a System file }
- BEGIN
- INC( NumFiles );
- GOTOXY( 1, YY );
- Write( PadR( DirInfo.Name, 15 ) );
- ADflag := 0;
- IF ModifyNowTF THEN BEGIN
- ASSIGN( F, DirInfo.Name );
- IF ( ProgramNum = ProgFD ) THEN BEGIN
- {$I-}
- RESET( F );
- {$I+}
- IF ( IOresult = 0 ) THEN BEGIN
- NewFtime;
- {$I-}
- SetFTime( F, Ftime ); { File must be OPENed to set its timestamp }
- {$I+}
- IF ( IOresult = 0 ) THEN Write( DTstr ) ELSE AccessDenied( 2 );
- END ELSE AccessDenied( 1 ); { fail on OpenFile, i.e. Reset }
- IF ( ADflag <> 1 ) THEN CLOSE( F );
- END ELSE BEGIN { FA }
- NA := NewAttrib;
- IF ( NA = DirInfo.Attr ) THEN BEGIN
- Write( 'No change needed.' );
- DEC( NumFiles );
- END ELSE BEGIN
- {$I-}
- SetFAttr( F, NA );
- {$I+}
- IF ( IOresult = 0 ) AND ( DosError = 0 )
- THEN Write( AttributeStr( DirInfo.Attr ),
- ' => ', AttributeStr( NA ) )
- ELSE AccessDenied( 3 );
- END; { ( NA <> DirInfo.Attr ) }
- END;
- IF CancelTF THEN BEGIN
- WriteLn( UserBreak );
- EXIT;
- END;
- END; { IF ModifyNowTF }
- CountUpdate;
- END; { If ( ProgramNum = ProgFA ) OR (not READONLY,HIDDEN,SYSTEM) }
- END; { If not DIRECTORY and not VOLUME }
- FindNext( DirInfo );
- END;
- END; { FOR I }
- IF ModifyNowTF THEN WriteLn ELSE GOTOXY( 1, YY );
- END; { ProcessFiles }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ ModifyFileTime: ║
- ║ * if only one file matches filespec on command line, ║
- ║ that file is touched without additional prompting ║
- ║ * if more than one file matches filespec, ║
- ║ user is told how many files would be touched, ║
- ║ then prompted for confirmation once before action is performed ║
- ║ on all files. ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE ModifyFileTime;
- VAR Ch : CHAR;
- XX, YY, XP, YP : BYTE;
- BEGIN
- IF ( NumFiles = 0 ) THEN WriteLn( 'No matching files found. Note: ' +
- ProgramName + ' ignores ReadOnly/Hidden/System files.' )
- ELSE IF ( ( NumFiles = 1 ) AND ( NOT ForcePromptTF ) ) THEN BEGIN
- GetSys; { Get System Date & Time into DateTime record SysDT }
- ProcessFiles( TRUE );
- END ELSE BEGIN
- IF AutoYesTF THEN Ch := 'Y' ELSE BEGIN
- Write( 'Timestamps of ', NumFiles,
- ' files will have ' );
- XX := WHEREX;
- YY := WHEREY;
- GOTOXY( XX, YY - 1);
- Write( #201, #32, UserDateStr ); {201} {218}
- GOTOXY( XX, YY );
- WriteLn( #202, #32, UserTimeStr ); {202} {193}
- XP := WHEREX;
- YP := WHEREY;
- Write( Proceed );
- REPEAT
- Ch := UpCase( ReadKey );
- IF ( Ch IN [ #3, #27 ] ) THEN Ch := 'N';
- UNTIL ( Ch IN [ 'Y', 'N' ] );
- IF ( Ch = 'N' ) THEN WriteLn( Ch );
- END;
- IF ( Ch = 'Y' ) THEN BEGIN
- GOTOXY( XP, YP );
- Write( Replicate( #32, LENGTH( Proceed ) ) );
- GOTOXY( XX, YY - 1 );
- Write( Replicate( #32, 2 + LENGTH( UserDateStr ) ) );
- GOTOXY( XX, YY );
- Write( Replicate( #32, 2 + LENGTH( UserTimeStr ) ) );
- GetSys; { Get System Date & Time into DateTime record SysDT }
- ProcessFiles( TRUE );
- END;
- END;
- END; { ModifyFileTime }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ ModifyFileAttr: ║
- ║ * if only one file matches filespec on command line, ║
- ║ that file's attributes are changed without additional prompting ║
- ║ * if more than one file matches filespec, ║
- ║ user is told how many files would be affected, ║
- ║ then prompted for confirmation once before action is performed ║
- ║ on all files. ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE ModifyFileAttr;
- VAR Ch : CHAR;
- XX, YY : BYTE;
- BEGIN
- IF ( NumFiles = 0 ) THEN WriteLn( 'No matching files found.' )
- ELSE IF ( ( NumFiles = 1 ) AND ( NOT ForcePromptTF ) ) THEN BEGIN
- ProcessFiles( TRUE );
- END ELSE BEGIN
- IF AutoYesTF THEN Ch := 'Y' ELSE BEGIN
- XX := WHEREX;
- YY := WHEREY;
- Write( 'Attributes of ', NumFiles,
- ' files will be changed. ' );
- Write( Proceed );
- REPEAT
- Ch := UpCase( ReadKey );
- IF ( Ch IN [ #3, #27 ] ) THEN Ch := 'N';
- UNTIL ( Ch IN [ 'Y', 'N' ] );
- IF ( Ch = 'N' ) THEN WriteLn( Ch );
- END;
- IF ( Ch = 'Y' ) THEN BEGIN
- GOTOXY( XX, YY );
- Write( BlankLine );
- ProcessFiles( TRUE );
- END;
- END;
- END; { ModifyFileAttr }
-
- PROCEDURE ListDirs( ProgNum : BYTE );
- CONST Banner = ' FileName Date Time Attr Size ';
- ChSortLast = #255;
- ChZipMark = #1;
- ChZipBullet = #254;
- VAR L, NL, NM, RowsShown, MaxScrnY, EnoughSpace : BYTE;
- S12 : Str12;
- CurDirNo, CurFileNo, FileCount : WORD;
- ShowFileTF, UserCancelTF, MultClusterTF, FinishedTF : BOOLEAN;
- FileBanner : PathStr;
- S : STRING;
- ClusterPart, ClusterCluster : LONGINT; { a little redundant,
- but Robin Williams says it's OK }
-
- FUNCTION NoOfDirsInDS : WORD;
- VAR I, DStotal : WORD;
-
- PROCEDURE AddDS( D : DirStr ); { D always begin with drive-letter:\ }
- VAR I : WORD;
- BEGIN
- IF ( DStotal > 0 ) THEN FOR I := 1 TO DStotal DO
- IF ( D = Index[ DirTotal + I ]^.P ) THEN EXIT;
- INC( DStotal );
- IF ( DStotal + DirTotal > FileTotal ) THEN AddNewIndex( FileTotal );
- Index[ DStotal + DirTotal ]^.P := D;
- END; { AddDS }
-
- PROCEDURE BuildDS( D : DirStr ); { D always begin with drive-letter:\ }
- VAR I, J : BYTE;
- BEGIN
- IF ( LENGTH( D ) = 3 ) THEN EXIT; { Skip root directory }
- D := D + '\';
- J := 0;
- FOR I := 1 TO LENGTH( D ) DO IF ( D[ I ] = '\' ) THEN BEGIN
- INC( J );
- IF ( J > 1 ) THEN AddDS( COPY( D, 1, I ) );
- END;
- END; { BuildDS }
-
- BEGIN { NoOfDirsInDS }
- DStotal := 0;
- FOR I := 1 TO DirTotal DO IF Index[ I ]^.TF THEN BuildDS( Index[ I ]^.P );
- NoOfDirsInDS := DStotal;
- END; { NoOfDirsInDS }
-
- FUNCTION FileInfo( DirInfo : SearchRec ) : PathStr;
- VAR DT : DateTime;
- SpaceUsed, OtherCS, Lines : LONGINT;
- LastInfoCol : Str20;
- AttrStr : Str12;
- FR : TEXT;
- A : STRING;
- I : BYTE;
- BEGIN
- UnpackTime( DirInfo.Time, DT );
- IF ( EnvVar = '' ) OR ( DrvSource = Index[ CurDirNo ]^.P[ 1 ] ) THEN
- SpaceUsed := ByteAdjust( DirInfo.Size, Clusticity[ 1 ] )
- ELSE BEGIN
- OtherCS := ByteClust( ORD( Index[ CurDirNo ]^.P[ 1 ] ) - 64 );
- IF ( OtherCS <> Clusticity[ 1 ] ) THEN MultClusterTF := TRUE;
- SpaceUsed := ByteAdjust( DirInfo.Size, OtherCS );
- END;
- INC( SpaceTotal[ 0 ], DirInfo.Size );
- INC( SpaceTotal[ 1 ], SpaceUsed );
- IF ( Clusticity[ 2 ] > 0 ) THEN
- INC( TargetSpace[ 1 ], ByteAdjust( DirInfo.Size, Clusticity[ 2 ] ) );
-
- FOR I := 1 TO MaxClustChk DO
- INC( SpaceCluster[ I, 1 ],
- ByteAdjust( DirInfo.Size, 1024 * Power( 2, I ) ) );
-
- AttrStr := AttributeStr( DirInfo.Attr );
-
- IF CountLnsTF THEN BEGIN
- Lines := 0; { # of lines }
- ASSIGN( FR, DirInfo.Name );
- {$I-}
- RESET( FR );
- {$I+}
- IF ( IOresult = 0 ) THEN BEGIN
- REPEAT
- READLN( FR, A );
- INC( Lines );
- UNTIL EOF( FR );
- CLOSE( FR );
- LastInfoCol := PrintUsing( Lines );
- END ELSE LastInfoCol := 'AccessDenied/L';
- INC( LinesTotal, Lines );
- END ELSE LastInfoCol := PrintUsing( SpaceUsed );
-
- FileInfo := PadR( DirInfo.Name, 15 ) +
- DateTimeStr( DT.Month, DT.Day, DT.Year ) +
- Replicate( #32, 3 ) +
- DateTimeStr( DT.Hour, DT.Min, DT.Sec ) +
- Replicate( #32, 3 ) + AttrStr +
- PadL( PrintUsing( DirInfo.Size ), 14 ) +
- PadL( LastInfoCol, 14 );
- END; { FileInfo }
-
- PROCEDURE GetFiles;
- CONST BT = 3; { # of files to read before changing "busy" character }
- VAR DirInfo : SearchRec;
- XX, NB, L : BYTE;
- NBT, LL : WORD;
-
- FUNCTION ChkAttribs : BOOLEAN;
- VAR TF : BOOLEAN;
- I : BYTE;
-
- FUNCTION ChkAttr( AttrVal : BYTE; Flag : SHORTINT ) : BOOLEAN;
- VAR RetTF : BOOLEAN;
- BEGIN
- RetTF := ( DirInfo.Attr AND AttrVal = AttrVal );
- IF ( Flag = 1 ) THEN ChkAttr := RetTF
- ELSE ChkAttr := NOT RetTF;
- END; { ChkAttr }
-
- BEGIN { ChkAttribs }
- TF := TRUE;
- FOR I := 1 TO 4 DO IF ( AttrFlag[ I ] <> 0 ) THEN TF := FALSE;
- IF TF THEN ChkAttribs := TRUE ELSE BEGIN
- FOR I := 1 TO 4 DO BEGIN
- IF ( AttrFlag[ I ] <> 0 ) THEN BEGIN
- TF := ChkAttr( AttrVal[ I ], AttrFlag[ I ] );
- IF TF THEN BREAK; { no need to check for further attributes }
- END;
- END; { FOR I }
- END;
- ChkAttribs := TF;
- END; { ChkAttribs }
-
- PROCEDURE AddFile( P : InfoStr );
- BEGIN
- INC( FileCount );
- IF ( DirTotal + FileCount > FileTotal )
- THEN AddNewIndex( FileTotal );
- Index[ DirTotal + FileCount ]^.P := P;
- END; { AddFile }
-
- PROCEDURE CheckMultSpec;
- VAR I : BYTE;
- Match12 : Str12;
- MatchTF : BOOLEAN;
-
- { ZipScan was adapted from source code found on CompuServe.
- Thanks to the author, whose name I cannot find. }
- PROCEDURE ZipScan;
- CONST SigFile = 'PK' + #3 + #4; {Signature = 'PK'+#1+#2 -> Central dir}
- Scanning = 'Scanning ZIP file ';
- VAR Zip : FILE;
- Signature : ARRAY[ 1..4 ] OF CHAR;
- ZFdata : ARRAY[ 1..26 ] OF CHAR;
- orig_time : INTEGER;
- orig_date : INTEGER;
- comp_size : LONGINT;
- uncomp : LONGINT;
- fn_size : INTEGER;
- extra : INTEGER;
- file_name : ARRAY[ 1..79 ] OF CHAR;
- Result : WORD;
- MatchSpecTF : BOOLEAN;
- S12 : Str12;
- I, XX, NB : BYTE;
- ScanMsg : DirStr;
- (* method : INTEGER; {0=stored,1=shrunk,2-5=reduced,6=imploded} *)
-
- FUNCTION Bin2Dec( StringVar : STRING ) : INTEGER;
- VAR RetVal : INTEGER;
- K, L : BYTE;
- BEGIN
- RetVal := 0;
- FOR K := 1 TO LENGTH( StringVar ) DO BEGIN
- IF ( StringVar[ K ] = '0' ) THEN L := 0 ELSE L := 1;
- RetVal := L + RetVal + RetVal;
- END;
- Bin2Dec := RetVal;
- END; { Bin2Dec }
-
- FUNCTION Bin2I( S2 : Str2 ) : INTEGER;
- BEGIN
- Bin2I := ORD( S2[ 1 ] ) + 256 * ORD( S2[ 2 ] );
- END; { Bin2I }
-
- FUNCTION Bin2L( S4 : Str4 ) : LONGINT;
- VAR L, M : LONGINT;
- K : BYTE;
- BEGIN
- L := 0;
- M := 1;
- FOR K := 1 TO 4 DO BEGIN
- L := L + ORD( S4[ K ] ) * M;
- IF (K < 4) THEN M := M * 256;
- END;
- Bin2L := L;
-
- (* does not work (yields negative values for large numbers)
- Bin2L := ORD(S4[1]) + 256 * ORD(S4[2])
- + 65536 * ORD(S4[3]) + 16777216 * ORD(S4[4]); *)
- END; { Bin2L }
-
- FUNCTION Dec2Bin( IntegerVar : INTEGER ) : Str16;
- VAR RetVal : Str16;
- Remainder, Quotient : INTEGER;
- BEGIN
- RetVal := NullStr;
- REPEAT
- Quotient := TRUNC( IntegerVar / 2 );
- Remainder := ABS( IntegerVar ) - 2 * ABS( Quotient );
- RetVal := COPY( '01', Remainder + 1, 1 ) + RetVal;
- IntegerVar := Quotient;
- UNTIL ( Quotient = 0 );
- WHILE ( LENGTH( RetVal ) < 16 ) DO RetVal := '0' + RetVal;
- Dec2Bin := RetVal;
- END; { Dec2Bin }
-
- FUNCTION DosDate( DateStamp : INTEGER ) : Str16;
- VAR yy,
- mm,
- dd : INTEGER;
- S16 : Str16;
- S4 : Str4;
- BEGIN
- S16 := Dec2Bin( DateStamp );
- yy := Bin2Dec( COPY( S16, 1, 7 ) ) + 1980;
- mm := Bin2Dec( COPY( S16, 8, 4 ) );
- dd := Bin2Dec( COPY( S16, 12, 5 ) );
- STR( mm : 2, S16 );
- STR( dd : 2, S4 );
- S16 := Zero( S16 ) + '-' + Zero( S4 );
- STR( yy : 4, S4 ); { @@ }
- S16 := S16 + '-' + S4;
- DosDate := S16;
- END; { DosDate }
-
- FUNCTION DosTime( TimeStamp : INTEGER ) : Str8;
- VAR hh,
- mm,
- ss : INTEGER;
- S16 : Str16;
- S2 : Str2;
- S8 : Str8;
- BEGIN
- S16 := Dec2Bin( TimeStamp );
- hh := Bin2Dec( COPY( S16, 1, 5 ) );
- mm := Bin2Dec( COPY( S16, 6, 6 ) );
- ss := Bin2Dec( COPY( S16, 12, 5 ) ); {2-second intervals (0-29)}
- STR( hh : 2, S8 );
- STR( mm : 2, S2 );
- S8 := Zero( S8 ) + ':' + Zero( S2 );
- STR( ss : 2, S2 );
- S8 := Zero( S8 ) + ':' + Zero( S2 );
- DosTime := S8 + ' ';
- END; { DosTime }
-
- BEGIN { ZipScan }
- MatchSpecTF := FALSE;
- ScanMsg := 'Scanning ZIP file ' + DirInfo.Name + '...';
- XX := WHEREX;
- NB := 0;
- Write( ScanMsg );
- ASSIGN( Zip, DirInfo.Name );
- FileMode := 0; { ReadOnly }
- {$I-}
- RESET( Zip, 1 );
- {$I+}
- { To examine IOresult, save it to IOvalue, a global INT var. }
- IF ( IOresult = 0 ) THEN BEGIN
- WHILE ( NOT EOF( Zip ) ) DO BEGIN
- BLOCKREAD( Zip, Signature, 4, Result );
- IF ( Signature = SigFile ) THEN BEGIN
- BLOCKREAD( Zip, ZFdata, 26, Result );
- comp_size := Bin2L( COPY( ZFdata, 15, 4 ) ); { compressed size }
- fn_size := Bin2I( COPY( ZFdata, 23, 2 ) ); { filename size }
- extra := Bin2I( COPY( ZFdata, 25, 2 ) ); { comment size }
-
- BLOCKREAD( Zip, file_name, fn_size, Result );
- Seek( Zip, FilePos( Zip ) + extra + comp_size );
- { skip past comments & compressed file }
-
- IF ( Result > 0 ) THEN BEGIN
- S12 := NullStr;
- FOR I := Result DOWNTO 1 DO
- IF ( file_name[ I ] = '/' ) THEN BREAK
- ELSE S12 := file_name[ I ] + S12;
-
- Match12 := File12( S12 );
- MatchSpecTF := Match( Match12, File12( PS ) );
- IF ( PSaddCount > 0 ) THEN FOR I := 1 TO PSaddCount
- DO MatchSpecTF := MatchSpecTF OR
- Match( Match12, File12( PSadd[ I ] ) );
- IF MatchSpecTF THEN BEGIN
- IF ( NOT MatchTF ) THEN BEGIN
- MatchTF := TRUE;
- AddFile( FileInfo( DirInfo ) );
- END;
- orig_time := Bin2I( COPY( ZFdata, 7, 2 ) ); { file time }
- orig_date := Bin2I( COPY( ZFdata, 9, 2 ) ); { file date }
- uncomp := Bin2L( COPY( ZFdata, 19, 4 ) ); { uncompressed size }
- AddFile( PadR( DirInfo.Name, 15 ) + ChSortLast +
- ChZipMark + ChZipBullet + ' ' + PadR( S12, 13 ) +
- DosDate( orig_date ) + Replicate( #32, 3 ) +
- DosTime( orig_time ) + ' in ZIP' +
- PadL( PrintUsing( uncomp ), 14 ) +
- PadL( PrintUsing( comp_size ), 14 ) );
- (*
- method := Bin2I( COPY( ZFdata, 5, 2) );
- { compression method, see VAR above }
- *)
- END; { IF MatchSpecTF }
- END; { ( Result > 0 ) }
- END; { If Signature = }
-
- INC( NB );
- IF ( NB > 4 ) THEN NB := 1;
- GOTOXY( LENGTH( ScanMsg ) + XX, WHEREY );
- Write( Busy[ NB ] );
-
- IF KeyPressed THEN BEGIN
- GOTOXY( XX, WHEREY );
- Write( Replicate( #32, LENGTH( ScanMsg ) + 1 ) );
- GOTOXY( XX, WHEREY );
- UserEscQuit;
- Write( ScanMsg );
- END; { KeyPressed }
-
- END; {WHILE (NOT EOF(Zip))}
- CLOSE( Zip );
- END; { IOresult = 0 }
- GOTOXY( XX, WHEREY );
- Write( Replicate( #32, LENGTH( ScanMsg ) + 1 ) );
- GOTOXY( XX, WHEREY );
- END; { ZipScan }
-
- BEGIN { CheckMultSpec }
- Match12 := File12( DirInfo.Name );
- MatchTF := Match( Match12, File12( PS ) );
- IF ( PSaddCount > 0 ) THEN FOR I := 1 TO PSaddCount
- DO MatchTF := MatchTF OR Match( Match12, File12( PSadd[ I ] ) );
- IF MatchTF THEN AddFile( FileInfo( DirInfo ) );
- IF ZipScanTF AND ( POS ( '.ZIP', DirInfo.Name ) > 0 ) THEN ZipScan;
- END; { CheckMultSpec }
-
- BEGIN { GetFiles }
- ChDir( Index[ CurDirNo ]^.P );
- FileCount := 0;
-
- { add a little on-screen activity to let user know that program is
- working (and not hung up) while reading (large) directories }
- Write( 'Reading directory ' );
- XX := WHEREX;
- NB := 0;
- NBT := 0;
-
- IF ( PSaddCount = 0 ) AND ( NOT ZipScanTF )
- THEN FindFirst( PS, AnyFile, DirInfo )
- ELSE FindFirst( '*.*', AnyFile, DirInfo );
- WHILE ( DosError = 0 ) DO BEGIN
- IF ( DirInfo.Attr AND Directory <> Directory ) AND { Not a Directory }
- ( DirInfo.Attr AND VolumeID <> VolumeID ) THEN BEGIN { Not a Volume label }
- IF ChkAttribs THEN BEGIN
- IF ( PSaddCount = 0 ) AND ( NOT ZipScanTF )
- THEN AddFile( FileInfo( DirInfo ) ) ELSE CheckMultSpec;
- END; { IF ChkAttribs }
- END; { NOT Directory or Volume }
-
- INC( NBT );
- IF ( ( NBT MOD BT ) = 1 ) THEN BEGIN
- INC( NB );
- IF ( NB > 4 ) THEN NB := 1;
- GOTOXY( XX, WHEREY );
- Write( Busy[ NB ] );
- END;
- UserEscQuit;
-
- FindNext( DirInfo );
- END; { WHILE }
-
- IF ( FileCount = 0 ) THEN Index[ CurDirNo ]^.TF := FALSE ELSE BEGIN
- Index[ CurDirNo ]^.TF := TRUE;
- ShowFileTF := TRUE;
- GOTOXY( 1, WHEREY );
- Write( 'Sorting directory...' );
- QuickSort( Index, DirTotal + 1, DirTotal + FileCount );
- IF ZipScanTF THEN
- FOR LL := ( DirTotal + 1 ) TO ( DirTotal + FileCount ) DO BEGIN
- L := POS( ChZipMark, Index[ LL ]^.P );
- IF ( L > 0 )
- THEN Index[ LL ]^.P := COPY( Index[ LL ]^.P, L + 1, LenInfoStr );
- END;
- CurFileNo := 0;
- INC( NumFiles, FileCount );
- END;
- GOTOXY( 1, WHEREY );
- Write( BlankLine );
- GOTOXY( 1, WHEREY );
- END; { GetFiles }
-
- PROCEDURE ShowDir( DirNum : WORD );
- VAR S12 : Str12;
- BEGIN
- STR( DirNum : L, S12 );
- S := PadR( S12 + '. ' + Index[ DirNum ]^.P, 79 );
- WriteLn( S );
- IF OutputTF THEN
- IF OutFlag[ 3 ] THEN WriteLn( FW, Index[ DirNum ]^.P )
- ELSE IF OutFlag[ 4 ] THEN WriteLn( FW, S );
- INC( RowsShown );
- END; { ShowDir }
-
- PROCEDURE ShowFile( FileNo : WORD );
- BEGIN
- S := PadR( Index[ DirTotal + FileNo ]^.P, 79 );
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 5 ] THEN WriteLn( FW, S );
- INC( RowsShown );
- END; { ShowFile }
-
- PROCEDURE ShowZipFile;
- VAR I : WORD;
- BEGIN
- FOR I := CurFileNo DOWNTO 1 DO
- IF ( NOT ( Index[ DirTotal + I ]^.P[ 1 ] = ChZipBullet ) ) THEN BEGIN
- ShowFile( I );
- EXIT;
- END;
- END;
-
- PROCEDURE UserInput;
- VAR S12 : Str12;
- DoneTF : BOOLEAN;
- IE : INTEGER;
- Ch : CHAR;
- BEGIN
- IF FinishedTF THEN BEGIN
- Write( 'End. ( # [exit to directory] / Any other key to quit ) -> ' );
- END ELSE BEGIN
- Write( 'Continue ( Y[es] / N[o] / C[ontinuous] / # [exit to directory] ) -> ' );
- END;
- S12 := '';
- DoneTF := FALSE;
- REPEAT
- Ch := UpCase( READKEY );
- CASE Ch OF
- #3, #27, 'N' : BEGIN
- S12 := '';
- UserCancelTF := TRUE;
- DoneTF := TRUE;
- END;
- #8 : IF ( LENGTH( S12 ) > 0 ) THEN BEGIN
- GOTOXY( WHEREX - 1, WHEREY );
- Write( #32 );
- GOTOXY( WHEREX - 1, WHEREY );
- S12 := COPY( S12, 1, LENGTH( S12 ) - 1 );
- END;
- #13, #32, 'Y' : DoneTF := TRUE;
- #48..#57 : BEGIN
- S12 := S12 + Ch;
- Write( Ch );
- END;
- 'C' : BEGIN
- AutoYesTF := TRUE;
- DoneTF := TRUE;
- END;
- END; { CASE Ch }
- UNTIL DoneTF;
- GOTOXY( 1, WHEREY );
- Write( BlankLine );
- GOTOXY( 1, WHEREY );
- VAL( S12, ExitDirNum, IE );
- IF ( IE <> 0 ) THEN ExitDirNum := 0;
- IF ( ExitDirNum > 0 ) THEN UserCancelTF := TRUE;
- END; { UserInput }
-
- BEGIN { ListDirs }
- CASE ProgNum OF
- ProgFF : MaxScrnY := MaxRows - 3;
- ProgLD : MaxScrnY := MaxRows - 2;
- END; { CASE ProgNum }
- UserCancelTF := FALSE;
- NumFiles := 0;
- GOTOXY( 41, WHEREY - 2 );
- WriteLn( DirTotal, ' directories.' );
- IF CountLnsTF THEN FileBanner := Banner + '# of Lines'
- ELSE FileBanner := Banner + 'Space Used';
- IF OutputTF THEN BEGIN
- ASSIGN( FW, OutputFile );
- REWRITE( FW );
- IF OutFlag[ 1 ] THEN WriteLn( FW, 'Command: ', CmdLine );
- IF OutFlag[ 2 ] THEN WriteLn( FW, DirTotal,
- ' directories starting from ', Index[ 1 ]^.P );
- END;
- IF ( ProgNum = ProgFF ) THEN BEGIN
- WriteLn( FileBanner );
- IF OutputTF THEN IF OutFlag[ 5 ] THEN WriteLn( FW, FileBanner );
- END;
- STR( DirTotal, S12 );
- L := LENGTH( S12 );
- CurDirNo := 0;
- RowsShown := 0;
- ShowFileTF := FALSE;
- DrvSource := Index[ 1 ]^.P[ 1 ];
- Clusticity[ 1 ] := ByteClust( ORD( DrvSource ) - 64 );
- IF ( NOT ( DrvTarget = #0 ) ) THEN BEGIN
- Clusticity[ 2 ] := ByteClust( ORD( DrvTarget ) - 64 );
- TargetSize[ 1 ] := DiskFree( ORD( DrvTarget ) - 64 );
- TargetSize[ 2 ] := DiskSize( ORD( DrvTarget ) - 64 );
- END;
- MultClusterTF := FALSE;
- EnoughSpace := 0;
-
- { main REPEAT: heart of directory/file listing routine }
- REPEAT
- { this REPEAT: one screenful at a time }
- REPEAT
- IF ShowFileTF THEN BEGIN
- INC( CurFileNo );
- ShowFile( CurFileNo );
- IF ( CurFileNo = FileCount ) THEN BEGIN
- ShowFileTF := FALSE;
- IF ( CurDirNo = DirTotal ) THEN BREAK;
- END;
- END ELSE BEGIN
- INC( CurDirNo );
- IF ( ProgNum = ProgFF ) THEN GetFiles ELSE Index[ CurDirNo ]^.TF := TRUE;
- IF Index[ CurDirNo ]^.TF THEN ShowDir( CurDirNo );
- IF ( CurDirNo = DirTotal ) AND ( NOT ShowFileTF ) THEN BREAK;
- END;
- UNTIL ( RowsShown = MaxScrnY ); { screenful }
-
- FinishedTF := ( CurDirNo = DirTotal ) AND ( NOT ShowFileTF );
- RowsShown := 0;
- IF ( NOT AutoYesTF ) THEN
- IF ( NOT FinishedTF ) OR ( NumFiles > 0 ) THEN UserInput;
- IF UserCancelTF THEN BREAK ELSE IF ( NOT FinishedTF ) THEN
- IF ( NOT AutoYesTF ) THEN BEGIN
- Write( PadR( ProgramName + #32 + Version + #32 + Extra, 79 ) );
- GOTOXY( 41, WHEREY );
- WriteLn( DirTotal, ' directories.' );
- IF ( ProgNum = ProgFF ) THEN BEGIN
- WriteLn( FileBanner );
- IF ShowFileTF THEN
- IF ( CurFileNo < FileCount ) THEN BEGIN
- ShowDir( CurDirNo );
- IF ( Index[ DirTotal + CurFileNo + 1 ]^.P[ 1 ] = ChZipBullet )
- THEN ShowZipFile;
- END;
- END; { ( NOT AutoYesTF ) }
- END;
- UNTIL FinishedTF; { full listing routine }
-
- IF ( ProgNum = ProgFF ) AND ( NOT UserCancelTF ) THEN BEGIN { Summary }
-
- IF ( NumFiles = 0 ) THEN BEGIN
- S := 'No files matching ' + PS;
- IF ZipScanTF THEN S := S + ' (or in .ZIP)';
- S := S + ', starting @ ' + Trim( Index[ 1 ]^.P ) + '.';
- WriteLn( S );
- IF OutputTF THEN BEGIN
- IF OutFlag[ 6 ] THEN WriteLn( FW, S );
- CLOSE( FW );
- END;
- EXIT;
- END;
-
- { add space used by directory structure }
- SpaceTotal[ 2 ] := SpaceTotal[ 1 ] + ( Clusticity[ 1 ] * NoOfDirsInDS );
- IF NOT ( DrvTarget = #0 ) THEN
- TargetSpace[ 2 ] := TargetSpace[ 1 ] + ( Clusticity[ 2 ] * NoOfDirsInDS );
- FOR NL := 1 TO MaxClustChk DO SpaceCluster[ NL, 2 ] :=
- SpaceCluster[ NL, 1 ] + 1024 * Power( 2, NL ) * NoOfDirsInDS;
-
- { determine largest value that will be printed,
- so we can line up the numbers
- so it doesn't look like a kitchen sink full of dirty dishes }
- FOR NL := 0 TO 2 DO
- L := MAX( L, LENGTH( PrintUsing( SpaceTotal[ NL ] ) ) );
- FOR NL := 1 TO 2 DO
- L := MAX( L, LENGTH( PrintUsing( TargetSize[ NL ] ) ) );
- FOR NL := 1 TO 2 DO
- L := MAX( L, LENGTH( PrintUsing( TargetSpace[ NL ] ) ) );
- FOR NL := 1 TO MaxClustChk DO
- L := MAX( L, LENGTH( PrintUsing( SpaceCluster[ NL, 2 ] ) ) );
- INC( L ); { one space to left to indent }
-
- STR( NumFiles, S12 );
- S := PadL( PrintUsing( SpaceTotal[ 0 ] ), L ) + ' bytes ';
- IF CountLnsTF THEN S := S + '(' + PrintUsing( LinesTotal ) + ' lines) ';
- S := S + 'in ' + S12 + ' files matching ' + PS;
- IF ( PSaddCount > 0 ) THEN FOR NL := 1 TO PSaddCount DO
- S := S + ',' + PSadd[ NL ];
- IF ZipScanTF THEN S := S + ' (or in .ZIP)';
- S := S + '.';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 6 ] THEN WriteLn( FW, S );
-
- IF ( EnvVar = '' ) THEN S12 := 'on Drive ' + DrvSource
- ELSE S12 := 'in PATH';
- S := PadL( PrintUsing( SpaceTotal[ 1 ] ), L ) +
- ' bytes of diskspace used ' + S12 + '. (cluster size: ';
- IF MultClusterTF THEN S := S + 'varies)'
- ELSE S := S + PrintUsing( Clusticity[ 1 ] ) + ')';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
-
- IF ( EnvVar = '' ) THEN S12 := 'on Drive ' + DrvSource
- ELSE S12 := 'in PATH';
- S := PadL( PrintUsing( SpaceTotal[ 2 ] ), L ) +
- ' bytes of diskspace used ' + S12 + '. (incl. directory tree)';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
-
- IF ClusterTF THEN FOR NL := 1 TO MaxClustChk DO BEGIN
- ClusterCluster := 1024 * Power( 2, NL );
- IF ( NL < 4 ) THEN BEGIN
- ClusterPart := 64 * Power( 2, NL );
- S12 := ' MB.';
- END ELSE BEGIN
- ClusterPart := Power( 2, ( NL - 4 ) );
- S12 := ' GB.';
- END;
- S := 'Clusters are ' + PrintUsing( ClusterCluster ) + ' bytes (' +
- PrintUsing( Power( 2, NL ) ) + ' KB) on HD partitions up to ' +
- PrintUsing( ClusterPart ) + S12;
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
- FOR NM := 1 TO 2 DO BEGIN
- IF ( NM = 1 ) THEN S12 := 'files only' ELSE S12 := 'plus tree.';
- S := PadL( PrintUsing( SpaceCluster[ NL, NM ] ), L ) +
- ' bytes (using cluster size: ' +
- PrintUsing( ClusterCluster ) + '), ' + S12;
- IF ( NM = 1 ) THEN S := S + ', ' +
- WastedSpace( SpaceTotal[ 0 ], SpaceCluster[ NL, NM ] ) +
- '% wasted.';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
- END;
- END;
-
- IF NOT ( DrvTarget = #0 ) THEN BEGIN
- S := PadL( PrintUsing( TargetSize[ 1 ] ), L ) +
- ' bytes available on Drive ' + DrvTarget + '. (total size: ' +
- PrintUsing( TargetSize[ 2 ] ) + ')';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
-
- S := PadL( PrintUsing( TargetSpace[ 1 ] ), L ) +
- ' bytes required to copy files to Drive ' + DrvTarget +
- '. (cluster size: ' + PrintUsing( Clusticity[ 2 ] ) + ')';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
-
- S := PadL( PrintUsing( TargetSpace[ 2 ] ), L ) +
- ' bytes required to copy files to Drive ' + DrvTarget +
- ', recreating tree.';
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
-
- IF ( TargetSize[ 1 ] >= TargetSpace[ 1 ] ) THEN INC( EnoughSpace );
- IF ( TargetSize[ 1 ] >= TargetSpace[ 2 ] ) THEN INC( EnoughSpace );
- CASE EnoughSpace OF
- 0 : S := 'Insufficient diskspace.';
- 1 : S := 'Sufficient diskspace for files but not to recreate directory tree.';
- 2 : S := 'Sufficient diskspace.';
- END; { CASE EnoughSpace }
- WriteLn( S );
- IF OutputTF THEN IF OutFlag[ 7 ] THEN WriteLn( FW, S );
- END; { NOT ( DrvTarget = #0 ) }
- END; { Summary ( ProgNum = ProgFF ) }
-
- IF OutputTF THEN BEGIN
- CLOSE( FW );
- (* @@ Scrn
- ScrPut1( TRUE ); { restore saved screen }
- *)
- WriteLn( ' (Output file: ' + OutputFile, ')' );
- END;
- IF ( ExitDirNum > 0 ) AND ( ExitDirNum <= DirTotal )
- THEN ChDir( Index[ ExitDirNum ]^.P );
- END; { ListDirs }
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ InitProg (initialize program) ║
- ║ * saves the current directory ║
- ║ * sets defaults for global variables ║
- ║ * dissects the command line arguments ║
- ║ * determines whether or not a file specification has been passed ║
- ║ * determines whether date / time passes is valid ║
- ║ * resolves ambiguous starting directory from the file specification ║
- ║ * build lists of directories (if /S passed) ║
- ║ * count files that would be touched ║
- ║ * sets CodeDate & CodeTime to simplify later time-stamping ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE InitProg;
- VAR P, EV : STRING;
- I, J : BYTE;
- StartDir : DirStr;
- DirCounter : WORD;
- S12 : Str12;
- DPM : ARRAY[ 1..12 ] OF BYTE;
- AnyAttrFlagTF, OutTF : BOOLEAN;
- Ch : CHAR;
-
- { InitDPM: set days per month according to year }
- PROCEDURE InitDPM( Year : WORD );
- BEGIN
- DPM[ 1 ] := 31;
- IF ( Year MOD 4 = 0 ) AND ( Year MOD 100 > 0 ) THEN DPM[ 2 ] := 29
- ELSE DPM[ 2 ] := 28;
- DPM[ 3 ] := 31;
- DPM[ 4 ] := 30;
- DPM[ 5 ] := 31;
- DPM[ 6 ] := 30;
- DPM[ 7 ] := 31;
- DPM[ 8 ] := 31;
- DPM[ 9 ] := 30;
- DPM[ 10 ] := 31;
- DPM[ 11 ] := 30;
- DPM[ 12 ] := 31;
- END; { InitDPM }
-
- PROCEDURE SetYY; { our most clever of procedures,
- why didn't we document it better? }
- CONST NumRows = 1;
- VAR I : BYTE;
- BEGIN
- FOR I := 1 TO NumRows DO WriteLn;
- YY := WHEREY - NumRows;
- END; { SetYY }
-
- { SetPS: take only the first parameter that look like a path-string }
- PROCEDURE SetPS( S : STRING );
- BEGIN
- IF ( PS = '' ) THEN PS := S ELSE BEGIN
- INC( PSaddCount );
- PSadd[ PSaddCount ] := S;
- END;
- END; { SetPS }
-
- PROCEDURE ChkFlag( S : STRING ); { / always stripped from S on entry }
- VAR Ch : CHAR;
- N : WORD;
- IE : INTEGER;
- SaveStr : STRING;
- NV : BYTE;
-
- { Invalid: If we detect an invalid "/" parameter, get even.
- Complain to the user 'bout it. }
- PROCEDURE Invalid( S12 : Str12 );
- BEGIN
- Error( 'Invalid ' + S12 + ': ' + SaveStr );
- END; { Invalid }
-
- { BreakStr: This function looks for a numeric value in string S
- that's in front of the delimiter character Ch.
- The existence of this delimiter must be verified prior
- to calling BreakStr (no error trap here, Jacq!).
- The numeric value is placed in N and
- S is returned as the substring following our delimiter.
- The return value IE is 0 if the VAL function successfully
- returned a numeric value (if not 0, N is bogus, dude!).
- This is used to interpret date & time strings such as
- 1-1-80 and 23:59:01. }
- FUNCTION BreakStr( VAR S : STRING; Delim : CHAR; VAR N : WORD ) : INTEGER;
- VAR IE : INTEGER;
- I : BYTE;
- BEGIN
- I := POS( Delim, S );
- VAL( COPY( S, 1, I - 1 ), N, IE );
- S := COPY( S, I + 1, 255 );
- BreakStr := IE;
- END; { BreakStr }
-
- FUNCTION CountDelim( S : STRING; Delim : CHAR ) : BYTE;
- VAR I, J : BYTE;
- BEGIN
- J := 0;
- FOR I := 1 TO LENGTH( S ) DO IF ( S[ I ] = Delim ) THEN INC( J );
- CountDelim := J;
- END; { CountDelim }
-
- PROCEDURE CheckDate;
- BEGIN
- IF ( ArgDT.Month < 1 ) OR ( ArgDT.Month > 12 ) THEN Invalid( 'Date' );
- InitDPM( ArgDT.Year );
- IF ( ArgDT.Day < 1 ) OR ( ArgDT.Day > DPM[ ArgDT.Month ] )
- THEN Invalid( 'Date' );
- END; { CheckDate }
-
- PROCEDURE CheckTime;
- BEGIN
- IF ( ArgDT.Hour < 0 ) OR ( ArgDT.Hour > 23 ) OR
- ( ArgDT.Min < 0 ) OR ( ArgDT.Min > 59 ) OR
- ( ArgDT.Sec < 0 ) OR ( ArgDT.Sec > 59 ) THEN Invalid( 'Time' );
- END; { CheckTime }
-
- BEGIN { ChkFlag }
-
- { chicken way of getting /SYS without modifying rest of procedure }
- IF ( COPY( S, 1, 3 ) = 'SYS' ) THEN BEGIN
- S := AttrSys + COPY( S, 4, 1 );
- END;
-
- Ch := S[ 1 ];
- S := COPY( S, 2, 255 );
- IF ( Ch = '?' ) THEN Help;
- IF ( Ch = 'S' ) THEN IF ( S = '-' ) THEN SubDirTF := FALSE
- ELSE SubDirTF := TRUE;
- IF ( Ch = 'Y' ) THEN AutoYesTF := TRUE;
- IF ( Ch = '1' ) THEN ForcePromptTF := TRUE;
-
- IF ( ProgramNum IN [ ProgFF, ProgFA ] ) THEN BEGIN
- FOR IE := 1 TO 4 DO IF ( Ch = AttrChar[ IE ] ) THEN BEGIN
- IF ( Ch = 'S' ) THEN IF COPY( S, 1, 2 ) = 'YS'
- THEN S := COPY( S, 3, 1 ) ELSE BREAK; { skip out of FOR }
- IF ( S = '+' ) OR
- ( ( ProgramNum = ProgFF ) AND ( S = '' ) )
- THEN AttrFlag[ IE ] := 1
- ELSE IF ( S = '-' ) THEN AttrFlag[ IE ] := -1;
- END; { FOR IE : attributes R A S H }
- IF ( ProgramNum = ProgFF ) THEN BEGIN
- IF ( Ch = 'E' ) AND ( LENGTH( S ) > 0 ) THEN BEGIN
- IF ( S[ 1 ] = ':' ) THEN S := COPY( S, 2, 255 );
- EnvVar := GetEnv( S );
- IF ( EnvVar = '' )
- THEN Error( 'Environment variable ' + S + ' not found.' );
- END; { 'E' }
- IF ( Ch = 'F' ) AND ( LENGTH( S ) > 0 ) THEN BEGIN
- IE := POS( ':', S );
- IF ( IE > 0 ) THEN IF ( LENGTH( S ) > IE )
- THEN DrvTarget := S[ IE + 1 ];
- END; { 'F' }
- IF ( Ch = 'L' ) THEN CountLnsTF := TRUE;
- IF ( Ch = 'C' ) THEN ClusterTF := TRUE;
- IF ( Ch = 'Z' ) THEN ZipScanTF := TRUE;
- END; { ( ProgramNum = ProgFF ) }
- END; { ( ProgramNum IN [ ProgFF, ProgFA ] ) }
-
- IF ( ProgramNum = ProgFD ) THEN BEGIN
- IF ( Ch = 'D' ) THEN BEGIN
- IF ( LENGTH( S ) = 0 ) THEN CodeDate := CmdValFlagOnly ELSE BEGIN
- CodeDate := CmdValArg; { flag & New Date }
- SaveStr := S;
- IF ( CountDelim( S, '-' ) < 2 ) THEN
- Invalid( 'Date' ); { mm-dd-yy req'd }
- IE := BreakStr( S, '-', N );
- IF ( IE = 0 ) THEN BEGIN
- ArgDT.Month := N;
- IE := BreakStr( S, '-', N );
- IF ( IE = 0 ) THEN BEGIN
- ArgDT.Day := N;
- IF ( LENGTH( S ) = 2 ) THEN S := '19' + S;
- VAL( S, ArgDT.Year, IE );
- IF ( IE <> 0 ) THEN Invalid( 'Date' ); { can't interpret year }
- END ELSE Invalid( 'Date' ); { can't interpret day # }
- END ELSE Invalid( 'Date' ); { can't interpret month # }
- CheckDate;
- END;
- END; { ( Ch = 'D' ) }
- IF ( Ch = 'T' ) THEN BEGIN
- IF ( LENGTH( S ) = 0 ) THEN CodeTime := CmdValFlagOnly ELSE BEGIN
- CodeTime := CmdValArg; { flag & New Time }
- SaveStr := S;
- WHILE ( CountDelim( S, ':' ) < 2 ) DO BEGIN { will accept 0, 1, or 2 colons in time }
- S := S + ':00';
- END;
- IE := BreakStr( S, ':', N );
- IF ( IE = 0 ) THEN BEGIN
- ArgDT.Hour := N;
- IE := BreakStr( S, ':', N );
- IF ( IE = 0 ) THEN BEGIN
- ArgDT.Min := N;
- VAL( S, ArgDT.Sec, IE );
- IF ( IE <> 0 ) THEN Invalid( 'Time' ); { can't interpret secs }
- END ELSE Invalid( 'Time' ); { can't interpret mins }
- END ELSE Invalid( 'Time' ); { can't interpret hour }
- CheckTime;
- END;
- END; { ( Ch = 'T' ) }
- END; { ( ProgNum = ProgFD ) }
-
- IF ( ProgramNum IN [ ProgFF, ProgLD ] ) THEN BEGIN
- IF ( Ch = 'W' ) AND ( LENGTH( S ) > 0 ) THEN BEGIN
- CASE S[ 1 ] OF
- ':' : BEGIN
- S := COPY( S, 2, 255 );
- IF ( LENGTH( S ) > 0 ) THEN BEGIN
- IF ( POS( '\', S ) = 0 )
- THEN OutputFile := FullName( CD, S )
- ELSE OutputFile := S;
- OutputTF := TRUE;
- END;
- END; { /W: }
- 'O' : OutOverAutoTF := TRUE;
- END; { CASE S[ 1 ] }
- IF ( NOT ( S[ 1 ] = ':' ) ) THEN BEGIN
- FOR N := 1 TO LENGTH( S ) DO BEGIN
- VAL( S[ N ], NV, IE );
- IF ( ( NV > 0 ) AND ( NV <= MaxOutOpts ) ) THEN BEGIN
- OutFlag[ NV ] := TRUE;
- OutTF := TRUE;
- END; { ( NV > 0 ) AND ( NV <= MaxOutOpts ) }
- END; { FOR N }
- END; { NOT ( S[ 1 ] = ':' ) }
- END; { /W }
- END; { ( ProgramNum IN [ ProgFF, ProgLD ] ) }
-
- END; { ChkFlag }
-
- PROCEDURE CheckFlags( S : STRING ); { S always begins with / on entry }
- VAR I : BYTE;
- BEGIN
- REPEAT
- S := COPY( S, 2, 255 );
- I := POS( '/', S );
- IF ( I = 0 ) THEN ChkFlag( S ) ELSE BEGIN
- ChkFlag( COPY( S, 1, I - 1 ) );
- S := COPY( S, I, 255 );
- END;
- UNTIL ( POS( '/', S ) = 0 );
- END; { CheckFlags }
-
- PROCEDURE AdjustCodes;
- BEGIN
- { ( similar true for CodeTime )
- As set in ChkFlag
- CodeDate : CmdValNone : /D not spec'd
- CmdValArg : /D with specified Date
- CmdValFlagOnly : /D only
-
- To be used in ProcessFiles ( for FD routine only ):
- CodeDate : ValFile : Retain file date
- ValSys : Set to system date
- ValArg : Set to specified date
- }
- IF ( CodeDate = CmdValNone ) THEN BEGIN
- IF ( CodeTime = CmdValNone ) THEN BEGIN
- CodeDate := ValSys;
- CodeTime := ValSys;
- END ELSE BEGIN
- CodeDate := ValFile;
- IF ( CodeTime = CmdValArg ) THEN CodeTime := ValArg
- ELSE CodeTime := ValSys;
- END;
- END ELSE BEGIN
- IF ( CodeDate = CmdValArg ) THEN CodeDate := ValArg
- ELSE CodeDate := ValSys;
- CASE CodeTime OF
- CmdValNone : CodeTime := ValFile;
- CmdValFlagOnly : CodeTime := ValSys;
- CmdValArg : CodeTime := ValArg;
- END; { CASE CodeTime }
- END;
-
- CASE CodeDate OF
- ValFile : UserDateStr := '(retain file date)';
- ValSys : UserDateStr := 'date set to system clock';
- ValArg : UserDateStr := 'date set to ' +
- DateTimeStr( ArgDT.Month, ArgDT.Day, ArgDT.Year );
- END; { CASE CodeDate }
- CASE CodeTime OF
- ValFile : UserTimeStr := '(retain file time)';
- ValSys : UserTimeStr := 'time set to system clock';
- ValArg : UserTimeStr := 'time set to ' +
- DateTimeStr( ArgDT.Hour, ArgDT.Min, ArgDT.Sec );
- END; { CASE CodeDate }
- END; { AdjustCodes }
-
- PROCEDURE WhichProgram;
- VAR PS : PathStr;
- D : DirStr;
- E : ExtStr;
- I, J : BYTE;
- BEGIN
- ProgramName := GetEnv( 'FADS' );
- IF ( ProgramName = '' ) THEN BEGIN
- PS := ParamStr( 0 ); { name of executable }
- FSplit( PS, D, ProgramName, E );
- END;
- ProgramName := UpStr( ProgramName );
- ProgramNum := 0;
- FOR I := 1 TO 5 DO FOR J := 1 TO MaxNames DO
- IF ( ProgramName = ProgNameArray[ I, J ] ) THEN ProgramNum := I;
- IF ( ProgramNum = ProgLC ) THEN BEGIN
- CountLnsTF := TRUE;
- SubDirTF := FALSE;
- ProgramNum := ProgFF; { easier just to leach off FF routine }
- END;
- IF ( ProgramNum = 0 ) THEN BEGIN
- WriteLn( 'Cannot identify program.' );
- ChameleonHelp;
- END;
- END; { WhichProgram }
-
- BEGIN { InitProg }
- GetDir( 0, CD ); { Save current directory }
- PS := ''; { Set our wonderful path specifier to a dull null }
- SubDirTF := TRUE; { if FF & LD, we search subdirs by default }
- FOR I := 0 TO 2 DO SpaceTotal[ I ] := 0;
- FOR I := 1 TO 2 DO TargetSize[ I ] := 0;
- FOR I := 1 TO 2 DO Clusticity[ I ] := 0;
- FOR I := 1 TO 2 DO TargetSpace[ I ] := 0;
- FOR I := 1 TO MaxClustChk DO SpaceCluster[ I, 1 ] := 0;
- FOR I := 1 TO MaxOutOpts DO OutFlag[ I ] := FALSE;
- AutoYesTF := FALSE;
- ForcePromptTF := FALSE;
- CodeDate := CmdValNone; { No /D flag }
- ArgDT.Year := 0;
- ArgDT.Month := 0;
- ArgDT.Day := 0;
- CodeTime := CmdValNone; { No /T flag }
- ArgDT.Hour := 0;
- ArgDT.Min := 0;
- ArgDT.Sec := 0;
- EnvVar := '';
- DrvTarget := #0;
- ExitDirNum := 0;
- CountLnsTF := FALSE;
- LinesTotal := 0;
- OutputFile := '';
- OutputTF := FALSE;
- OutOverAutoTF := FALSE;
- BlankLine := Replicate( #32, 79 );
- ClusterTF := FALSE;
- PSaddCount := 0;
- CmdLine := ParamStr( 0 );
- ZipScanTF := FALSE;
- OutTF := FALSE;
-
- WhichProgram; { If we can't figure it out here, let 'em weep!
- We're exiting to DOS after a message }
-
- WriteLn( ProgramName, #32, Version, #32, Extra ); { the sign-on }
-
- (* @@ Scrn
- { save current screen }
- ScrGet1;
- *)
-
- { First, decipher command line parameters }
- IF ( ParamCount = 0 ) THEN BEGIN
- IF ( ProgramNum IN [ ProgFD, ProgFA ] ) THEN Help ELSE PS := CD;
- END ELSE BEGIN { Parameters passed }
- IF ( ProgramNum IN [ ProgFD, ProgFA ] )
- THEN SubDirTF := FALSE; { no subdirectory search for Touch unless /S }
- FOR I := 1 TO ParamCount DO BEGIN
- CmdLine := CmdLine + #32 + ParamStr( I );
- P := UpStr( ParamStr( I ) );
- J := POS( '/', P );
- IF ( J = 0 ) THEN SetPS( P ) ELSE BEGIN
- CheckFlags( COPY( P, J, 255 ) );
- P := COPY( P, 1, J - 1 );
- IF NOT ( P = '' ) THEN SetPS( P );
- END;
- END; { FOR I }
- IF ( PS = '' ) THEN CASE ProgramNum OF
- ProgFF : PS := '*.*';
- ProgLD : PS := CD;
- END; { CASE ProgramNum }
- END; { ( ParamCount > 0 ) }
-
- { Fix OutFlags - whether or not we use 'em }
- IF ( NOT OutTF ) THEN
- FOR I := 1 TO MaxOutOpts DO OutFlag[ I ] := TRUE;
- IF OutFlag[ 4 ] THEN OutFlag[ 3 ] := FALSE;
-
- { If program is set to change file attributes AND no attributes are spec'd }
- IF ( ProgramNum IN [ ProgFA ] ) THEN BEGIN
- AnyAttrFlagTF := FALSE;
- FOR I := 1 TO 4 DO IF ( AttrFlag[ I ] <> 0 ) THEN AnyAttrFlagTF := TRUE;
- IF ( NOT AnyAttrFlagTF ) THEN Help;
- END;
-
- { If we don't have a pathspec (and no default), give the user Help }
- IF ( PS = '' ) THEN Help;
-
- { If an output file was specified, prompt to overwrite an existing one }
- IF OutputTF THEN BEGIN
- IF ( FSearch( OutputFile, '' ) = '' ) THEN Ch := 'Y' ELSE BEGIN
- IF OutOverAutoTF THEN Ch := 'Y' ELSE BEGIN
- Write( 'Overwrite existing file ', OutputFile, ' (Y/N) -> ' );
- REPEAT
- Ch := UpCase( READKEY );
- UNTIL ( Ch IN [ 'Y', 'N', #27 ] );
- GOTOXY( 1, WHEREY );
- Write( BlankLine );
- GOTOXY( 1, WHEREY );
- END;
- END;
- IF ( Ch = 'Y' )
- THEN AutoYesTF := TRUE { Assume user want no prompts if Output file }
- ELSE OutputTF := FALSE;
- END; { OutputTF }
-
- { Determine starting directory }
- { Using GetDir will turn strings such as C:\DOCS\..\DOCS into a nice clean
- simple C:\DOCS. }
- DirTotal := 0; { If we don't set this here,
- we might as well go pogo sticking at the beach. }
- {$I-}
- ChDir( PS ); { 1st, attempt to log to directory named PS }
- {$I+}
- IF ( IOresult = 0 ) THEN BEGIN
- StartDir := PS;
- PS := '*.*';
- GetDir( 0, StartDir ); { set StartDir to standard directory string }
- END ELSE BEGIN
- I := LastPos( '\', PS );
- IF ( I = 0 ) THEN BEGIN
- I := POS( ':', PS );
- IF ( I = 0 ) THEN StartDir := CD ELSE BEGIN { only drive specified, no directory }
- StartDir := COPY( PS, 1, I );
- PS := COPY( PS, I + 1, 79 );
- {$I-}
- ChDir( StartDir ); { change to drive }
- {$I+}
- IF NOT ( IOresult = 0 ) THEN Error( 'Invalid drive specified ' + StartDir );
- GetDir( 0, StartDir ); { make drive's CD our StartDir }
- END;
- END ELSE BEGIN { a directory & filespec have been specified }
- StartDir := COPY( PS, 1, I );
- IF ( LENGTH( StartDir ) > 3 ) THEN IF ( StartDir[ I - 1 ] <> ':' )
- THEN StartDir := COPY( StartDir, 1, I - 1 );
- PS := COPY( PS, I + 1, 79 );
- {$I-}
- ChDir( StartDir ); { change to starting directory }
- {$I+}
- IF NOT ( IOresult = 0 ) THEN Error( 'Invalid directory specified ' + StartDir );
- GetDir( 0, StartDir ); { set StartDir to standard directory string }
- END;
- END;
- AddDirToList( StartDir, FALSE );
-
- { Determine screen row for status info }
- SetYY;
-
- { Build list of directories if /S }
- DirCounter := 0;
- IF ( EnvVar = '' ) THEN BEGIN
- IF SubDirTF THEN REPEAT
- INC( DirCounter );
- GetDirList( Index[ DirCounter ]^.P );
- UNTIL ( DirCounter = DirTotal );
- END ELSE BEGIN
- DirTotal := 0;
- EV := UpStr( EnvVar );
- WHILE ( LENGTH( EV ) > 0 ) DO BEGIN
- I := POS( ';', EV );
- IF ( I = 0 ) THEN BEGIN
- AddDirToList( EV, TRUE );
- EV := '';
- END ELSE BEGIN
- P := COPY( EV, 1, I - 1 );
- EV := COPY( EV, I + 1, 255 );
- IF NOT ( P = '' ) THEN AddDirToList( P, TRUE );
- END;
- END;
- END;
- GOTOXY( 1, YY );
- STR( DirTotal, S12 );
- Write( Replicate( #32, LENGTH( S12 ) + 19 ) );
- FileTotal := DirTotal; { FF adds to end of DirTotal }
-
- IF ( ProgramNum IN [ ProgFD, ProgFA ] ) THEN BEGIN
- { Count # of files that would be touched }
- ProcessFiles( FALSE ); { count files only - Touch only }
-
- { Examine CodeDate & CodeTime set in ChkFlag
- and modify to values more easily used in ProcessFiles - Touch only }
- IF ( ProgramNum = ProgFD ) THEN AdjustCodes;
- END ELSE BEGIN
- WriteLn;
- QuickSort( Index, 1, DirTotal );
- END;
- END;
-
- {
- ╔══════════════════════════════════════════════════════════════════════════╗
- ║ ExitProg (exit program) ║
- ║ * disposes of pointers to directory names that were stored on the heap ║
- ║ (be a good little programmer and clean up after yourself) ║
- ╚══════════════════════════════════════════════════════════════════════════╝
- }
-
- PROCEDURE ExitProg;
- VAR I : WORD;
- BEGIN
- FOR I := FileTotal DOWNTO 1 DO DISPOSE( Index[ I ] );
- IF ( ExitDirNum = 0 ) THEN ChDir( CD );
- END;
-
- BEGIN { MAIN }
- InitProg;
- IF ( ProgramNum IN [ ProgFD ] ) THEN ModifyFileTime;
- IF ( ProgramNum IN [ ProgFA ] ) THEN ModifyFileAttr;
- IF ( ProgramNum IN [ ProgLD, ProgFF ] ) THEN ListDirs( ProgramNum );
- ExitProg;
- END.
-